পাইথনের asyncio ফিউচার আয়ত্ত করুন। শক্তিশালী, উচ্চ-পারফরম্যান্স অ্যাপ্লিকেশন তৈরির জন্য নিম্ন-স্তরের অ্যাসিঙ্ক ধারণা, বাস্তব উদাহরণ এবং উন্নত কৌশলগুলি অন্বেষণ করুন।
Asyncio ফিউচার উন্মোচিত: পাইথনে নিম্ন-স্তরের অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ে একটি গভীর অনুসন্ধান
আধুনিক পাইথন ডেভেলপমেন্টের জগতে, async/await
সিনট্যাক্স উচ্চ-পারফরম্যান্স, I/O-বাউন্ড অ্যাপ্লিকেশন তৈরির জন্য একটি ভিত্তি হয়ে উঠেছে। এটি সমান্তরাল কোড লেখার একটি পরিষ্কার, মার্জিত উপায় সরবরাহ করে যা প্রায় অনুক্রমিক দেখায়। কিন্তু এই উচ্চ-স্তরের সিনট্যাকটিক সুগারের নিচে একটি শক্তিশালী এবং মৌলিক প্রক্রিয়া রয়েছে: Asyncio ফিউচার। যদিও আপনি প্রতিদিন কাঁচা ফিউচারগুলির সাথে ইন্টারঅ্যাক্ট নাও করতে পারেন, তবে সেগুলিকে বোঝা পাইথনে অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংকে সত্যিই আয়ত্ত করার চাবিকাঠি। এটি একটি গাড়ির ইঞ্জিন কীভাবে কাজ করে তা শেখার মতো; গাড়ি চালানোর জন্য আপনার এটি জানার দরকার নেই, তবে আপনি যদি একজন মাস্টার মেকানিক হতে চান তবে এটি অপরিহার্য।
এই ব্যাপক নির্দেশিকা asyncio
এর উপর থেকে পর্দা সরাবে। আমরা ফিউচার কী, কো-রুটিন এবং টাস্ক থেকে তারা কীভাবে আলাদা, এবং কেন এই নিম্ন-স্তরের আদিমটি পাইথনের অ্যাসিঙ্ক্রোনাস ক্ষমতাগুলির ভিত্তি, তা অন্বেষণ করব। আপনি যদি একটি জটিল রেস কন্ডিশন ডিবাগ করেন, পুরানো কলব্যাক-ভিত্তিক লাইব্রেরিগুলির সাথে একত্রিত হন, বা কেবল অ্যাসিঙ্ক সম্পর্কে গভীর বোঝার লক্ষ্য রাখেন, তবে এই নিবন্ধটি আপনার জন্য।
আসলে Asyncio ফিউচার কী?
মূলত, একটি asyncio.Future
হল একটি অবজেক্ট যা একটি শেষ ফলাফলকে উপস্থাপন করে। এটিকে একটি প্লেসহোল্ডার, একটি প্রতিশ্রুতি, অথবা একটি মূল্যের জন্য একটি রসিদ হিসাবে ভাবুন যা এখনও উপলব্ধ নয়। যখন আপনি এমন একটি অপারেশন শুরু করেন যা সম্পূর্ণ হতে সময় নেবে (যেমন একটি নেটওয়ার্ক অনুরোধ বা একটি ডেটাবেস ক্যোয়ারী), আপনি অবিলম্বে একটি ফিউচার অবজেক্ট ফেরত পেতে পারেন। আপনার প্রোগ্রাম অন্যান্য কাজ চালিয়ে যেতে পারে, এবং যখন অপারেশনটি অবশেষে শেষ হয়, তখন ফলাফল (বা একটি ত্রুটি) সেই ফিউচার অবজেক্টের মধ্যে স্থাপন করা হবে।
একটি সহায়ক বাস্তব বিশ্বের উপমা হল একটি ব্যস্ত ক্যাফেতে কফি অর্ডার করা। আপনি আপনার অর্ডার দেন এবং অর্থ প্রদান করেন, এবং বারিস্তা আপনাকে একটি অর্ডার নম্বর সহ একটি রসিদ দেয়। আপনার কাছে এখনও আপনার কফি নেই, তবে আপনার কাছে রসিদ আছে—একটি কফির প্রতিশ্রুতি। আপনি এখন কাউন্টারে অলসভাবে দাঁড়িয়ে না থেকে একটি টেবিল খুঁজতে বা আপনার ফোন দেখতে যেতে পারেন। যখন আপনার কফি প্রস্তুত হয়, আপনার নম্বর ডাকা হয়, এবং আপনি চূড়ান্ত ফলাফলের জন্য আপনার রসিদ 'পুনরুদ্ধার' করতে পারেন। রসিদটি হল ফিউচার।
একটি ফিউচারের মূল বৈশিষ্ট্যগুলির মধ্যে রয়েছে:
- নিম্ন-স্তরীয়: টাস্কের তুলনায় ফিউচারগুলি একটি আরও আদিম বিল্ডিং ব্লক। তারা সহজাতভাবে কোনও কোড কীভাবে চালাতে হয় তা জানে না; তারা কেবল একটি ফলাফলের জন্য ধারক যা পরে সেট করা হবে।
- অ্যাওয়েটেবল: একটি ফিউচারের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য হল এটি একটি অ্যাওয়েটেবল অবজেক্ট। এর মানে হল আপনি এর উপর
await
কীওয়ার্ড ব্যবহার করতে পারেন, যা আপনার কো-রুটিনের কার্যকারিতা বন্ধ করে দেবে যতক্ষণ না ফিউচারের একটি ফলাফল থাকে। - স্টেটফুল: একটি ফিউচার তার জীবনচক্র জুড়ে কয়েকটি স্বতন্ত্র অবস্থার মধ্যে একটিতে বিদ্যমান থাকে: পেন্ডিং, বাতিল, বা সম্পন্ন।
ফিউচার বনাম কো-রুটিন বনাম টাস্ক: বিভ্রান্তি দূর করা
asyncio
তে নতুন ডেভেলপারদের জন্য সবচেয়ে বড় বাধাগুলির মধ্যে একটি হল এই তিনটি মূল ধারণার মধ্যে সম্পর্ক বোঝা। তারা গভীরভাবে আন্তঃসংযুক্ত তবে বিভিন্ন উদ্দেশ্য পরিবেশন করে।
1. কো-রুটিন
একটি কো-রুটিন হল async def
দিয়ে সংজ্ঞায়িত একটি ফাংশন। যখন আপনি একটি কো-রুটিন ফাংশন কল করেন, তখন এটি তার কোড কার্যকর করে না। পরিবর্তে, এটি একটি কো-রুটিন অবজেক্ট ফেরত দেয়। এই অবজেক্টটি গণনার জন্য একটি ব্লুপ্রিন্ট, তবে একটি ইভেন্ট লুপ দ্বারা চালিত না হওয়া পর্যন্ত কিছুই ঘটে না।
উদাহরণ:
async def fetch_data(url): ...
fetch_data("http://example.com")
কল করলে আপনি একটি কো-রুটিন অবজেক্ট পাবেন। আপনি এটিকে await
না করা পর্যন্ত বা এটিকে একটি টাস্ক হিসাবে শিডিউল না করা পর্যন্ত এটি নিষ্ক্রিয় থাকে।
2. টাস্ক
একটি asyncio.Task
হল যা আপনি একটি কো-রুটিনকে ইভেন্ট লুপে সমান্তরালভাবে চালানোর জন্য শিডিউল করতে ব্যবহার করেন। আপনি asyncio.create_task(my_coroutine())
ব্যবহার করে একটি টাস্ক তৈরি করেন। একটি টাস্ক আপনার কো-রুটিনকে মোড়ানো করে এবং ইভেন্ট লুপের সুযোগ পাওয়ার সাথে সাথেই এটিকে "ব্যাকগ্রাউন্ডে" চালানোর জন্য অবিলম্বে শিডিউল করে। এখানে বোঝার গুরুত্বপূর্ণ বিষয় হল যে একটি টাস্ক ফিউচারের একটি সাবক্লাস। এটি একটি বিশেষায়িত ফিউচার যা একটি কো-রুটিনকে কীভাবে চালাতে হয় তা জানে।
যখন মোড়ানো কো-রুটিন সম্পন্ন হয় এবং একটি মান ফেরত দেয়, তখন টাস্কের (যা, মনে রাখবেন, একটি ফিউচার) ফলাফল স্বয়ংক্রিয়ভাবে সেট হয়। যদি কো-রুটিন একটি ব্যতিক্রম উত্থাপন করে, তবে টাস্কের ব্যতিক্রম সেট হয়।
3. ফিউচার
একটি সাধারণ asyncio.Future
আরও মৌলিক। একটি টাস্কের মতো নয়, এটি কোনও নির্দিষ্ট কো-রুটিনের সাথে আবদ্ধ নয়। এটি কেবল একটি খালি প্লেসহোল্ডার। অন্য কিছু—আপনার কোডের অন্য একটি অংশ, একটি লাইব্রেরি, বা ইভেন্ট লুপ নিজেই—পরে এর ফলাফল বা ব্যতিক্রম স্পষ্টভাবে সেট করার জন্য দায়ী। টাস্কগুলি আপনার জন্য এই প্রক্রিয়াটি স্বয়ংক্রিয়ভাবে পরিচালনা করে, তবে একটি কাঁচা ফিউচারের সাথে, ব্যবস্থাপনা ম্যানুয়াল।
পার্থক্য পরিষ্কার করার জন্য এখানে একটি সারাংশ টেবিল রয়েছে:
ধারণা | এটি কী | এটি কীভাবে তৈরি হয় | প্রাথমিক ব্যবহার |
---|---|---|---|
কো-রুটিন | async def দিয়ে সংজ্ঞায়িত একটি ফাংশন; একটি জেনারেটর-ভিত্তিক গণনা ব্লুপ্রিন্ট। |
async def my_func(): ... |
অ্যাসিঙ্ক্রোনাস লজিক সংজ্ঞায়িত করা। |
টাস্ক | একটি ফিউচার সাবক্লাস যা একটি কো-রুটিনকে মোড়ানো করে এবং ইভেন্ট লুপে চালায়। | asyncio.create_task(my_func()) |
কো-রুটিনগুলি সমান্তরালভাবে চালানো ("ফায়ার অ্যান্ড ফরগেট")। |
ফিউচার | একটি নিম্ন-স্তরের অ্যাওয়েটেবল অবজেক্ট যা একটি শেষ ফলাফলকে উপস্থাপন করে। | loop.create_future() |
কলব্যাক-ভিত্তিক কোডের সাথে ইন্টারফেসিং; কাস্টম সিঙ্ক্রোনাইজেশন। |
সংক্ষেপে: আপনি কো-রুটিন লেখেন। আপনি টাস্ক ব্যবহার করে সেগুলিকে সমান্তরালভাবে চালান। টাস্ক এবং অন্তর্নিহিত I/O অপারেশন উভয়ই সম্পন্ন হওয়ার সংকেত দেওয়ার জন্য ফিউচারকে মৌলিক প্রক্রিয়া হিসাবে ব্যবহার করে।
একটি ফিউচারের জীবনচক্র
একটি ফিউচার সহজ কিন্তু গুরুত্বপূর্ণ অবস্থার সেটের মধ্য দিয়ে রূপান্তরিত হয়। এই জীবনচক্র বোঝা কার্যকরভাবে তাদের ব্যবহার করার চাবিকাঠি।
অবস্থা 1: পেন্ডিং
যখন একটি ফিউচার প্রথম তৈরি হয়, তখন এটি পেন্ডিং অবস্থায় থাকে। এটির কোনও ফলাফল এবং কোনও ব্যতিক্রম নেই। এটি কারও দ্বারা সম্পন্ন হওয়ার জন্য অপেক্ষা করছে।
import asyncio
async def main():
# Get the current event loop
loop = asyncio.get_running_loop()
# Create a new Future
my_future = loop.create_future()
print(f"Is the future done? {my_future.done()}") # Output: False
# To run the main coroutine
asyncio.run(main())
অবস্থা 2: সমাপ্তি (একটি ফলাফল বা ব্যতিক্রম সেট করা)
একটি পেন্ডিং ফিউচার দুটি উপায়ের মধ্যে একটিতে সম্পন্ন করা যেতে পারে। এটি সাধারণত ফলাফলের "প্রযোজক" দ্বারা করা হয়।
1. set_result()
দিয়ে একটি সফল ফলাফল সেট করা:
যখন অ্যাসিঙ্ক্রোনাস অপারেশন সফলভাবে সম্পন্ন হয়, তখন এর ফলাফল এই পদ্ধতি ব্যবহার করে ফিউচারের সাথে সংযুক্ত হয়। এটি ফিউচারকে সম্পন্ন অবস্থায় রূপান্তরিত করে।
2. set_exception()
দিয়ে একটি ব্যতিক্রম সেট করা:
যদি অপারেশন ব্যর্থ হয়, তখন একটি ব্যতিক্রম অবজেক্ট ফিউচারের সাথে সংযুক্ত হয়। এটি ফিউচারকে সম্পন্ন অবস্থায় রূপান্তরিত করে। যখন অন্য একটি কো-রুটিন `await`স এই ফিউচারকে, তখন সংযুক্ত ব্যতিক্রমটি উত্থাপিত হবে।
অবস্থা 3: সম্পন্ন
একবার একটি ফলাফল বা একটি ব্যতিক্রম সেট হয়ে গেলে, ফিউচারকে সম্পন্ন হিসাবে বিবেচনা করা হয়। এর অবস্থা এখন চূড়ান্ত এবং পরিবর্তন করা যাবে না। আপনি এটি future.done()
পদ্ধতি দিয়ে পরীক্ষা করতে পারেন। যে কোনও কো-রুটিন যা এই ফিউচারকে await
করছিল, তারা এখন জেগে উঠবে এবং তাদের কার্যকারিতা পুনরায় শুরু করবে।
(ঐচ্ছিক) অবস্থা 4: বাতিল
একটি পেন্ডিং ফিউচার future.cancel()
পদ্ধতি কল করে বাতিলও করা যেতে পারে। এটি অপারেশনটি পরিত্যাগ করার একটি অনুরোধ। যদি বাতিলকরণ সফল হয়, তবে ফিউচার একটি বাতিল অবস্থায় প্রবেশ করে। যখন অ্যাওয়েট করা হয়, একটি বাতিল ফিউচার একটি CancelledError
উত্থাপন করবে।
ফিউচার নিয়ে কাজ করা: ব্যবহারিক উদাহরণ
তত্ত্ব গুরুত্বপূর্ণ, তবে কোড এটিকে বাস্তব করে তোলে। আসুন দেখি কিভাবে আপনি নির্দিষ্ট সমস্যা সমাধানের জন্য কাঁচা ফিউচার ব্যবহার করতে পারেন।
উদাহরণ 1: একটি ম্যানুয়াল প্রযোজক/ভোক্তা পরিস্থিতি
এটি একটি ক্লাসিক উদাহরণ যা মূল যোগাযোগ প্যাটার্নকে প্রদর্শন করে। আমাদের একটি কো-রুটিন (`consumer`) থাকবে যা একটি ফিউচারের জন্য অপেক্ষা করবে, এবং অন্য একটি (`producer`) যা কিছু কাজ করবে এবং তারপর সেই ফিউচারে ফলাফল সেট করবে।
import asyncio
import time
async def producer(future):
print("Producer: Starting to work on a heavy calculation...")
await asyncio.sleep(2) # Simulate I/O or CPU-intensive work
result = 42
print(f"Producer: Calculation finished. Setting result: {result}")
future.set_result(result)
async def consumer(future):
print("Consumer: Waiting for the result...")
# The 'await' keyword pauses the consumer here until the future is done
result = await future
print(f"Consumer: Got the result! It's {result}")
async def main():
loop = asyncio.get_running_loop()
my_future = loop.create_future()
# Schedule the producer to run in the background
# It will work on completing my_future
asyncio.create_task(producer(my_future))
# The consumer will wait for the producer to finish via the future
await consumer(my_future)
asyncio.run(main())
# Expected Output:
# Consumer: Waiting for the result...
# Producer: Starting to work on a heavy calculation...
# (2-second pause)
# Producer: Calculation finished. Setting result: 42
# Consumer: Got the result! It's 42
এই উদাহরণে, ফিউচার একটি সিঙ্ক্রোনাইজেশন পয়েন্ট হিসাবে কাজ করে। `consumer` কে ফলাফল সরবরাহ করে তা জানে না বা গ্রাহ্য করে না; এটি কেবল ফিউচারটি নিজেই গ্রাহ্য করে। এটি প্রযোজক এবং ভোক্তাকে বিচ্ছিন্ন করে, যা সমান্তরাল সিস্টেমগুলিতে একটি খুব শক্তিশালী প্যাটার্ন।
উদাহরণ 2: কলব্যাক-ভিত্তিক API গুলিকে সংযুক্ত করা
এটি কাঁচা ফিউচারের জন্য সবচেয়ে শক্তিশালী এবং সাধারণ ব্যবহারের ক্ষেত্রগুলির মধ্যে একটি। অনেক পুরানো লাইব্রেরি (বা যে লাইব্রেরিগুলিকে C/C++ এর সাথে ইন্টারফেস করতে হবে) `async/await` নেটিভ নয়। পরিবর্তে, তারা একটি কলব্যাক-ভিত্তিক শৈলী ব্যবহার করে, যেখানে আপনি সম্পন্ন হওয়ার পরে কার্যকর করার জন্য একটি ফাংশন পাস করেন।
ফিউচারগুলি এই API গুলিকে আধুনিকীকরণের জন্য একটি নিখুঁত সেতু সরবরাহ করে। আমরা একটি র্যাপার ফাংশন তৈরি করতে পারি যা একটি অ্যাওয়েটেবল ফিউচার ফেরত দেয়।
ধরা যাক আমাদের কাছে একটি কাল্পনিক উত্তরাধিকার ফাংশন legacy_fetch(url, callback)
রয়েছে যা একটি URL ফেচ করে এবং সম্পন্ন হলে `callback(data)` কল করে।
import asyncio
from threading import Timer
# --- This is our hypothetical legacy library ---
def legacy_fetch(url, callback):
# This function is not async and uses callbacks.
# We simulate a network delay using a timer from the threading module.
print(f"[Legacy] Fetching {url}... (This is a blocking-style call)")
def on_done():
data = f"Some data from {url}"
callback(data)
# Simulate a 2-second network call
Timer(2, on_done).start()
# -----------------------------------------------
async def modern_fetch(url):
"""Our awaitable wrapper around the legacy function."""
loop = asyncio.get_running_loop()
future = loop.create_future()
def on_fetch_complete(data):
# This callback will be executed in a different thread.
# To safely set the result on the future belonging to the main event loop,
# we use loop.call_soon_threadsafe.
loop.call_soon_threadsafe(future.set_result, data)
# Call the legacy function with our special callback
legacy_fetch(url, on_fetch_complete)
# Await the future, which will be completed by our callback
return await future
async def main():
print("Starting modern fetch...")
data = await modern_fetch("http://example.com")
print(f"Modern fetch complete. Received: '{data}'")
asyncio.run(main())
এই প্যাটার্নটি অবিশ্বাস্যভাবে দরকারী। `modern_fetch` ফাংশন সমস্ত কলব্যাক জটিলতা লুকিয়ে রাখে। `main` এর দৃষ্টিকোণ থেকে, এটি কেবল একটি নিয়মিত `async` ফাংশন যা অ্যাওয়েট করা যেতে পারে। আমরা সফলভাবে একটি লিগ্যাসি API কে "ফিউচারাইজড" করেছি।
দ্রষ্টব্য: যখন কলব্যাক একটি ভিন্ন থ্রেড দ্বারা কার্যকর হয়, যেমন asyncio এর সাথে একত্রিত নয় এমন লাইব্রেরিগুলিতে I/O অপারেশনের সাথে সাধারণ, তখন loop.call_soon_threadsafe
এর ব্যবহার অত্যন্ত গুরুত্বপূর্ণ। এটি নিশ্চিত করে যে future.set_result
asyncio ইভেন্ট লুপের প্রেক্ষাপটে নিরাপদে কল করা হয়।
কখন কাঁচা ফিউচার ব্যবহার করবেন (এবং কখন করবেন না)
উপলব্ধ শক্তিশালী উচ্চ-স্তরের অ্যাবস্ট্রাকশনগুলির সাথে, কখন একটি ফিউচারের মতো নিম্ন-স্তরের সরঞ্জাম ব্যবহার করতে হবে তা জানা গুরুত্বপূর্ণ।
কাঁচা ফিউচার ব্যবহার করুন যখন:
- কলব্যাক-ভিত্তিক কোডের সাথে ইন্টারফেসিং: উপরের উদাহরণে দেখানো হয়েছে, এটিই প্রাথমিক ব্যবহারের ক্ষেত্র। ফিউচারগুলি আদর্শ সেতু।
- কাস্টম সিঙ্ক্রোনাইজেশন আদিম তৈরি করা: যদি আপনাকে নির্দিষ্ট আচরণ সহ একটি ইভেন্ট, লক বা কিউ-এর আপনার নিজস্ব সংস্করণ তৈরি করতে হয়, তবে ফিউচারগুলি হবে মূল উপাদান যার উপর আপনি তৈরি করবেন।
- একটি ফলাফল একটি কো-রুটিন ছাড়া অন্য কিছু দ্বারা উত্পাদিত হয়: যদি একটি ফলাফল একটি বাহ্যিক ইভেন্ট উৎস দ্বারা তৈরি হয় (যেমন, অন্য প্রক্রিয়া থেকে একটি সংকেত, একটি ওয়েবসকেট ক্লায়েন্ট থেকে একটি বার্তা), তবে একটি ফিউচার asyncio জগতে সেই পেন্ডিং ইভেন্টটিকে উপস্থাপন করার নিখুঁত উপায়।
কাঁচা ফিউচার পরিহার করুন (পরিবর্তে টাস্ক ব্যবহার করুন) যখন:
- আপনি কেবল একটি কো-রুটিনকে সমান্তরালভাবে চালাতে চান: এটি
asyncio.create_task()
এর কাজ। এটি কো-রুটিনকে মোড়ানো, এটিকে শিডিউল করা এবং এর ফলাফল বা ব্যতিক্রমকে টাস্ক (যা একটি ফিউচার) এ প্রচার করা পরিচালনা করে। এখানে একটি কাঁচা ফিউচার ব্যবহার করা চাকা পুনরায় আবিষ্কার করার মতো হবে। - সমান্তরাল অপারেশনগুলির গ্রুপ পরিচালনা করা: একাধিক কো-রুটিন চালানো এবং তাদের সম্পূর্ণ হওয়ার জন্য অপেক্ষা করার জন্য,
asyncio.gather()
,asyncio.wait()
, এবংasyncio.as_completed()
এর মতো উচ্চ-স্তরের API গুলি অনেক নিরাপদ, আরও পঠনযোগ্য এবং কম ত্রুটি-প্রবণ। এই ফাংশনগুলি সরাসরি কো-রুটিন এবং টাস্কগুলির উপর কাজ করে।
উন্নত ধারণা এবং সমস্যা
ফিউচার এবং ইভেন্ট লুপ
একটি ফিউচার যে ইভেন্ট লুপে তৈরি হয়েছিল তার সাথে অভ্যন্তরীণভাবে সংযুক্ত। একটি `await future` এক্সপ্রেশন কাজ করে কারণ ইভেন্ট লুপ এই নির্দিষ্ট ফিউচার সম্পর্কে জানে। এটি বোঝে যে যখন এটি একটি পেন্ডিং ফিউচারে একটি `await` দেখে, তখন এটি বর্তমান কো-রুটিনকে স্থগিত করবে এবং অন্যান্য কাজ করার জন্য দেখবে। যখন ফিউচারটি অবশেষে সম্পন্ন হয়, তখন ইভেন্ট লুপ জানে কোন স্থগিত কো-রুটিনকে জাগিয়ে তুলতে হবে।
এই কারণেই আপনাকে সর্বদা loop.create_future()
ব্যবহার করে একটি ফিউচার তৈরি করতে হবে, যেখানে loop
হল বর্তমানে চলমান ইভেন্ট লুপ। বিভিন্ন ইভেন্ট লুপ জুড়ে (বা সঠিক সিঙ্ক্রোনাইজেশন ছাড়া বিভিন্ন থ্রেডে) ফিউচার তৈরি এবং ব্যবহার করার চেষ্টা করলে ত্রুটি এবং অপ্রত্যাশিত আচরণ হবে।
`await` আসলে কী করে
যখন পাইথন ইন্টারপ্রেটার result = await my_future
এর সম্মুখীন হয়, তখন এটি কিছু ধাপ সম্পন্ন করে:
- এটি
my_future.__await__()
কল করে, যা একটি ইটারেটর ফেরত দেয়। - এটি ফিউচারটি ইতিমধ্যে সম্পন্ন হয়েছে কিনা তা পরীক্ষা করে। যদি তা হয়, তবে এটি ফলাফল পায় (বা ব্যতিক্রম উত্থাপন করে) এবং স্থগিত না করে চালিয়ে যায়।
- যদি ফিউচারটি পেন্ডিং থাকে, তবে এটি ইভেন্ট লুপকে বলে: "আমার কার্যকারিতা স্থগিত করুন, এবং এই নির্দিষ্ট ফিউচার সম্পন্ন হলে দয়া করে আমাকে জাগিয়ে তুলুন।"
- ইভেন্ট লুপ তখন দায়িত্ব নেয়, অন্যান্য প্রস্তুত টাস্ক চালায়।
- একবার
my_future.set_result()
বাmy_future.set_exception()
কল করা হলে, ইভেন্ট লুপ ফিউচারটিকে সম্পন্ন হিসাবে চিহ্নিত করে এবং স্থগিত কো-রুটিনকে লুপের পরবর্তী পুনরাবৃত্তিতে পুনরায় শুরু করার জন্য শিডিউল করে।
সাধারণ ভুল: ফিউচারকে টাস্কের সাথে গুলিয়ে ফেলা
একটি সাধারণ ভুল হল যখন একটি টাস্ক সঠিক সরঞ্জাম হয় তখন একটি ফিউচার দিয়ে একটি কো-রুটিনের কার্যকারিতা ম্যানুয়ালি পরিচালনা করার চেষ্টা করা।
ভুল উপায় (অতিরিক্ত জটিল):
# This is verbose and unnecessary
async def main_wrong():
loop = asyncio.get_running_loop()
future = loop.create_future()
# A separate coroutine to run our target and set the future
async def runner():
try:
result = await some_other_coro()
future.set_result(result)
except Exception as e:
future.set_exception(e)
# We have to manually schedule this runner coroutine
asyncio.create_task(runner())
# Finally, we can await our future
final_result = await future
সঠিক উপায় (একটি টাস্ক ব্যবহার করে):
# A Task does all of the above for you!
async def main_right():
# A Task is a Future that automatically drives a coroutine
task = asyncio.create_task(some_other_coro())
# We can await the task directly
final_result = await task
যেহেতু Task
হল Future
এর একটি সাবক্লাস, তাই দ্বিতীয় উদাহরণটি কেবল পরিষ্কারই নয় বরং কার্যকারিতা হিসাবে সমতুল্য এবং আরও দক্ষ।
উপসংহার: Asyncio এর ভিত্তি
Asyncio ফিউচার হল পাইথনের অ্যাসিঙ্ক্রোনাস ইকোসিস্টেমের অনুল্লেখিত নায়ক। এটিই নিম্ন-স্তরের আদিম যা async/await
এর উচ্চ-স্তরের জাদু সম্ভব করে তোলে। যদিও আপনার প্রতিদিনের কোডিং মূলত কো-রুটিন লেখা এবং সেগুলিকে টাস্ক হিসাবে শিডিউল করা নিয়ে জড়িত থাকবে, তবে ফিউচারগুলি বোঝা আপনাকে সবকিছু কীভাবে সংযুক্ত হয় সে সম্পর্কে গভীর অন্তর্দৃষ্টি দেবে।
ফিউচার আয়ত্ত করার মাধ্যমে, আপনি নিম্নলিখিত ক্ষমতাগুলি অর্জন করেন:
- আত্মবিশ্বাসের সাথে ডিবাগ করুন: যখন আপনি একটি
CancelledError
বা একটি কো-রুটিন দেখতে পান যা কখনও ফেরত আসে না, তখন আপনি অন্তর্নিহিত ফিউচার বা টাস্কের অবস্থা বুঝতে পারবেন। - যে কোনও কোড একত্রিত করুন: আপনার এখন যে কোনও কলব্যাক-ভিত্তিক API মোড়ানো এবং এটিকে আধুনিক অ্যাসিঙ্ক বিশ্বে একটি প্রথম-শ্রেণীর নাগরিক করার ক্ষমতা রয়েছে।
- পরিশীলিত সরঞ্জাম তৈরি করুন: ফিউচার সম্পর্কে জ্ঞান হল আপনার নিজস্ব উন্নত সমান্তরাল এবং সমান্তরাল প্রোগ্রামিং কনস্ট্রাক্ট তৈরি করার প্রথম ধাপ।
সুতরাং, পরের বার যখন আপনি asyncio.create_task()
বা await asyncio.gather()
ব্যবহার করবেন, তখন পর্দার আড়ালে অক্লান্ত পরিশ্রম করা নম্র ফিউচারকে প্রশংসা করার জন্য একটু সময় নিন। এটিই একটি দৃঢ় ভিত্তি যার উপর শক্তিশালী, স্কেলেবল এবং মার্জিত অ্যাসিঙ্ক্রোনাস পাইথন অ্যাপ্লিকেশন তৈরি করা হয়।